package xj.graph2d.shapes;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.SelfLoop;

public class OrthogonalSelfLoop4 
  extends OrthogonalLineShape 
  implements SelfLoop, ShapeConstants {

  public OrthogonalSelfLoop4(float x1, float y1, float x2, float y2,
			     float width, float height, int direction) {
    this(x1, y1, x2, y2, width, height, direction, false);
  }

  public OrthogonalSelfLoop4(float x1, float y1, float x2, float y2,
			     float width, float height, int direction, 
			     boolean createComp) {
    super(!(direction >= INVERSE_DIRECTION), x1, y1, createComp);
    allowSelfLoop = true;
    this.height = height;
    this.width = width;
    this.direction = direction;
    float x = (x1 + x2) / 2;
    float y = (y1 + y2) / 2;
    boolean inverse = (direction >= INVERSE_DIRECTION);
    switch (direction) {
    case NORTH_EAST:
    case NORTH_EAST + INVERSE_DIRECTION:
      x = Math.max(x1, x2) + width;
      y = Math.min(y1, y2) - height;
      break;
    case NORTH_WEST:
    case NORTH_WEST + INVERSE_DIRECTION:
      x = Math.min(x1, x2) - width;
      y = Math.min(y1, y2) - height;
      break;
    case SOUTH_EAST:
    case SOUTH_EAST + INVERSE_DIRECTION:
      x = Math.max(x1, x2) + width;
      y = Math.max(y1, y2) + height;
      break;
    case SOUTH_WEST:
    case SOUTH_WEST + INVERSE_DIRECTION:
      x = Math.min(x1, x2) - width;
      y = Math.max(y1, y2) + height;
      break;
    }
    float x3, y3, x4, y4;
    if (inverse) {
      x3 = x;
      y3 = (y1 + y) / 2;
      x4 = (x2 + x) / 2;
      y4 = y;
    } else {
      x3 = (x1 + x) / 2;
      y3 = y;
      x4 = x;
      y4 = (y2 + y) / 2;
    }
    addPoint(x3, y3);
    addPoint(x4, y4);
    addPoint(x2, y2);
  }

  public OrthogonalSelfLoop4(boolean createComp) {
    this(20, 20, NORTH, createComp);
  }

  public OrthogonalSelfLoop4(float width, float height, int direction,
			     boolean createComp) {
    super(true, createComp);
    allowSelfLoop = true;
    this.height = height;
    this.width = width;
    this.direction = direction;
  }

  @Override
  public DrawShape makeInstance() {
    return new OrthogonalSelfLoop4(width, height, direction, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    if (s != null) {
      super.copyTo(s);
      if (s instanceof OrthogonalSelfLoop4) {
	OrthogonalSelfLoop4 r = (OrthogonalSelfLoop4) s;
	r.height = height;
	r.width = width;
	r.direction = direction;
      }
    }
  }

  public boolean isOrthogonal() {
    return true; 
  }

  public boolean isOneSided() {
    return false; 
  }

  public int getDirection() {
    return direction;
  }

  public void setDirection(int direction) {
    this.direction = direction;
    // positionLabel();
    // adjustContent();
  }

  public float getHeight() {
    return height;
  }

  public void setHeight(float height) {
    this.height = height;
  }

  public float getWidth() {
    return width;
  }

  public void setWidth(float width) {
    this.width = width;
  }

  public boolean isAdjustable() {
    return adjustable;
  }

  public void setAdjustable(boolean b) {
    adjustable = b;
  }

  public void adjustDirection(Rectangle2D bounds, float x1, float y1, float x2, float y2) {
    boolean inverse = false;
    if (bounds != null) {
      float nx = (float) bounds.getX();
      float ny = (float) bounds.getY();
      float nw = (float) bounds.getWidth();
      float nh = (float) bounds.getHeight();
      float div = nx + nw / 2;
      float cx = (x1 + x2) / 2;
      if (x1 < x2) {
	if (cx < div) {
	  inverse = true;
	}
      } else {
	if (cx >= div) {
	  inverse = true;
	}
      }
    }

    if (y1 < y2) {
      if (x1 < x2) {
	direction = (inverse ? (SOUTH_WEST + INVERSE_DIRECTION) : NORTH_EAST);
      } else {
	direction = (inverse ? (SOUTH_EAST + INVERSE_DIRECTION) : NORTH_WEST);
      }
    } else {
      if (x1 < x2) {
	direction = (inverse ? (NORTH_WEST + INVERSE_DIRECTION) : SOUTH_EAST);
      } else {
	direction = (inverse ? (NORTH_EAST + INVERSE_DIRECTION) : SOUTH_WEST);
      }
    }
    setStartVertical(!inverse);
  }

  @Override
  public void adjustSelfLoop() {
    if (debug) {
      System.out.println("OrthogonalSelfLoop4.adjustSelfLoop()");
    }
    float x1 = getX();
    float y1 = getY();
    float x2, y2;
    Point2D p = getEnd2();
    if (p != null) {
      x2 = (float) p.getX();
      y2 = (float) p.getY();

      float x = (x1 + x2) / 2;
      float y = (y1 + y2) / 2;
      boolean inverse = (direction >= INVERSE_DIRECTION);
      switch (direction) {
      case NORTH_EAST:
      case NORTH_EAST + INVERSE_DIRECTION:
	x = Math.max(x1, x2) + width;
	y = Math.min(y1, y2) - height;
	break;
      case NORTH_WEST:
      case NORTH_WEST + INVERSE_DIRECTION:
	x = Math.min(x1, x2) - width;
	y = Math.min(y1, y2) - height;
	break;
      case SOUTH_EAST:
      case SOUTH_EAST + INVERSE_DIRECTION:
	x = Math.max(x1, x2) + width;
	y = Math.max(y1, y2) + height;
	break;
      case SOUTH_WEST:
      case SOUTH_WEST + INVERSE_DIRECTION:
	x = Math.min(x1, x2) - width;
	y = Math.max(y1, y2) + height;
	break;
      }
      float x3, y3, x4, y4;
      if (inverse) {
	x3 = x;
	y3 = (y1 + y) / 2;
	x4 = (x2 + x) / 2;
	y4 = y;
      } else {
	x3 = (x1 + x) / 2;
	y3 = y;
	x4 = x;
	y4 = (y2 + y) / 2;
      }
      setPoint(1, x3, y3);
      setPoint(2, x4, y4);

      adapter.invalidateBounds();
      calculateBounds();
    }
  }

  @Override
  public void adjustEnds(GraphNodeBase src, 
			 GraphNodeBase des, 
			 boolean adjustCtrPts) {
    if (debug) {
      System.out.println("OrthogonalSelfLoop4.adjustEnds() adjustable=" + adjustable);
    }
    super.adjustEnds(src, des, adjustCtrPts);
    if (!adjustable) { 
      adjustSelfLoop();
    }
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("height", height);
    attrs.put("width", width);
    attrs.put("direction", direction);
    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("height");
      try {
	height = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
      }
      s = node.getAttribute("width");
      try {
	width = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
      }
      s = node.getAttribute("direction");
      try {
	direction = Integer.parseInt(s);
      } catch (NumberFormatException ex) {
      }
    }
  }

  protected float height;
  protected float width;
  protected int direction;
  protected boolean adjustable = true;

}
